library(here)
source(here("utils/data_processing.R"))
source(here("utils/figures.R"))
Introduction
- Goal is to model diagnoses from each criteria based on how
frequently they co-occur together within a single 10-point differential
diagnosis iteration.
- The rationale is that an ideal set of criteria will tend to result
in a relatively similar set of diagnoses in each iteration and thus a
small set of highly co-occurring diagnoses.
- Conversely, a poor set of criteria will generate highly variable
diagnoses in each iteration and rates of co-occurrence will be
comparatively lower.
Import and process data
df_gpt3.5 <- read_model("gpt-3.5-turbo-1106", icd = FALSE)
df_gpt4.0 <- read_model("gpt-4-turbo-preview", icd = FALSE)
df_claude3_haiku_t1.0 <- read_model("claude-3-haiku-20240307_t1-0", icd = FALSE)
df_claude3_opus_t1.0 <- read_model("claude-3-opus-20240229_t1-0", icd = FALSE)
df_gemini1.0_pro_t1.0 <- read_model("gemini-1.0-pro-002_t1-0", icd = FALSE)
df_gemini1.5_pro_t1.0 <- read_model("gemini-1.5-pro-001_t1-0", icd = FALSE)
df_gpt3.5_icd <- read_model("gpt-3.5-turbo-1106", icd = TRUE)
df_gpt4.0_icd <- read_model("gpt-4-turbo-preview", icd = TRUE)
df_claude3_haiku_t1.0_icd <- read_model("claude-3-haiku-20240307_t1-0", icd = TRUE)
df_claude3_opus_t1.0_icd <- read_model("claude-3-opus-20240229_t1-0", icd = TRUE)
df_gemini1.0_pro_t1.0_icd <- read_model("gemini-1.0-pro-002_t1-0", icd = TRUE)
df_gemini1.5_pro_t1.0_icd <- read_model("gemini-1.5-pro-001_t1-0", icd = TRUE)
# Tally each pair of co-occuring diagnoses within each criteria
# Original responses
df_gpt3.5_codiag <- create_codiagnosis_df(df_gpt3.5, remove_singletons = T)
df_gpt4.0_codiag <- create_codiagnosis_df(df_gpt4.0, remove_singletons = T)
df_claude3_haiku_t1.0_codiag <- create_codiagnosis_df(df_claude3_haiku_t1.0, remove_singletons = T)
df_claude3_opus_t1.0_codiag <- create_codiagnosis_df(df_claude3_opus_t1.0, remove_singletons = T)
df_gemini1.0_pro_t1.0_codiag <- create_codiagnosis_df(df_gemini1.0_pro_t1.0, remove_singletons = T)
df_gemini1.5_pro_t1.0_codiag <- create_codiagnosis_df(df_gemini1.5_pro_t1.0, remove_singletons = T)
# ICD converted responses
df_gpt3.5_icd_codiag <- create_codiagnosis_df(df_gpt3.5_icd, remove_singletons = T)
df_gpt4.0_icd_codiag <- create_codiagnosis_df(df_gpt4.0_icd, remove_singletons = T)
df_claude3_haiku_t1.0_icd_codiag <- create_codiagnosis_df(df_claude3_haiku_t1.0_icd, remove_singletons = T)
df_claude3_opus_t1.0_icd_codiag <- create_codiagnosis_df(df_claude3_opus_t1.0_icd, remove_singletons = T)
df_gemini1.0_pro_t1.0_icd_codiag <- create_codiagnosis_df(df_gemini1.0_pro_t1.0_icd, remove_singletons = T)
df_gemini1.5_pro_t1.0_icd_codiag <- create_codiagnosis_df(df_gemini1.5_pro_t1.0_icd, remove_singletons = T)
df_gpt4.0_codiag
Graph visualization
Exploring layouts
- To determine clearest visualization of nodes and edges
# Selecting a layout
top_n <- 200
seed <- 1234
layouts <- c("fr", "dh", "kk", "stress", "graphopt")
graph_top_gpt4 <- make_codiagnosis_graph(df_gpt4.0_codiag, n_diagnoses = top_n)
for (l in layouts){
set.seed(seed)
plt <- centrality_graph(graph_top_gpt4, layout = l)
plt <- plt + ggtitle("GPT4", subtitle = sprintf("Layout %s", l))
print(plt)
}






Individual model data
Original responses
top_n <- 100
seed <- 321
graph_layout <- "stress"
codiag_graph_wrapper <- function(data){
set.seed(seed)
data <- make_codiagnosis_graph(data, n_diagnoses = top_n)
centrality_graph(data, layout = graph_layout)
}
codiag_graph_wrapper(df_gpt3.5_codiag)+ ggtitle("ChatGPT 3.5", subtitle = sprintf("Top %s", top_n))


codiag_graph_wrapper(df_gpt4.0_codiag)+ ggtitle("ChatGPT 4.0", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_haiku_t1.0_codiag)+ ggtitle("Claude 3 Haiku", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_opus_t1.0_codiag)+ ggtitle("Claude 3 Opus", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.0_pro_t1.0_codiag)+ ggtitle("Gemini 1.0 Pro", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.5_pro_t1.0_codiag)+ ggtitle("Gemini 1.5 Pro", subtitle = sprintf("Top %s", top_n))

ICD converted respones
codiag_graph_wrapper(df_gpt3.5_icd_codiag)+ ggtitle("ChatGPT 3.5 ICD", subtitle = sprintf("Top %s", top_n))


codiag_graph_wrapper(df_gpt4.0_icd_codiag)+ ggtitle("ChatGPT 4.0 ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_haiku_t1.0_icd_codiag)+ ggtitle("Claude 3 Haiku ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_opus_t1.0_icd_codiag)+ ggtitle("Claude 3 Opus ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.0_pro_t1.0_icd_codiag)+ ggtitle("Gemini 1.0 Pro ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.5_pro_t1.0_icd_codiag)+ ggtitle("Gemini 1.5 Pro ICD", subtitle = sprintf("Top %s", top_n))

Combined model data
Original responses
set.seed(seed)
multi_make_codiagnosis_graph(threshold_method = "individual", top_n = 100, layout="stress",
df_gpt3.5_codiag, df_gpt4.0_codiag,
df_claude3_haiku_t1.0_codiag, df_claude3_opus_t1.0_codiag,
df_gemini1.0_pro_t1.0_codiag, df_gemini1.5_pro_t1.0_codiag)


multi_make_codiagnosis_graph(threshold_method = "average", top_n = 100, layout="stress",
df_gpt3.5_codiag, df_gpt4.0_codiag,
df_claude3_haiku_t1.0_codiag, df_claude3_opus_t1.0_codiag,
df_gemini1.0_pro_t1.0_codiag, df_gemini1.5_pro_t1.0_codiag)


ICD converted responses
set.seed(seed)
multi_make_codiagnosis_graph(threshold_method = "individual", top_n = 100, layout="stress",
df_gpt3.5_icd_codiag, df_gpt4.0_icd_codiag,
df_claude3_haiku_t1.0_icd_codiag, df_claude3_opus_t1.0_icd_codiag,
df_gemini1.0_pro_t1.0_icd_codiag, df_gemini1.5_pro_t1.0_icd_codiag)


multi_make_codiagnosis_graph(threshold_method = "average", top_n = 100, layout="stress",
df_gpt3.5_icd_codiag, df_gpt4.0_icd_codiag,
df_claude3_haiku_t1.0_icd_codiag, df_claude3_opus_t1.0_icd_codiag,
df_gemini1.0_pro_t1.0_icd_codiag, df_gemini1.5_pro_t1.0_icd_codiag)


Evaluate edge density
- Edge density represents the total number of edges in a graph
relative to the total number of possible edges in the
graph
- When all possible edges are present, edge density = 1
- When no edges are present, edge density = 0
- Dense co-occurrence networks represent criteria that generate highly
reproducible diagnoses. A sparse co-occurrence represents a high degree
of variability
Combined model data
Original responses
multi_edge_density_plot(
df_gpt3.5_codiag,
df_gpt4.0_codiag,
df_claude3_haiku_t1.0_codiag,
df_claude3_opus_t1.0_codiag,
df_gemini1.0_pro_t1.0_codiag,
df_gemini1.5_pro_t1.0_codiag
)

ICD converted responses

Plot centrality
calculate_subgraph_centrality <- function(g, centrality_fun = "centrality_eigen"){
data.frame(criteria = g %>% activate(edges) %>% pull(criteria) %>% unique()) %>%
mutate(sub_graphs = map(criteria, function(c){
g %>% as_data_frame() %>% filter(criteria == c) %>% as_tbl_graph(directed = F) %>%
activate(nodes) %>% mutate(centrality = get(centrality_fun)()) %>% data.frame()
})) %>%
unnest(sub_graphs) %>%
pivot_wider(names_from = "name", values_from = "centrality", values_fill = 0) %>%
column_to_rownames("criteria")
}
graph_all_gpt4 <- make_codiagnosis_graph(df_gpt4.0_codiag, n_diagnoses = top_n)
df_centr_gpt4 <- calculate_subgraph_centrality(graph_all_gpt4)
centrality_similarity <- function(data){
data %>%
rownames_to_column("criteria") %>%
format_criteria() %>%
column_to_rownames("criteria") %>%
as.matrix() %>%
t() %>%
lsa::cosine() %>%
as.data.frame() %>%
rownames_to_column("V1") %>%
pivot_longer(-V1, names_to = "V2", values_to = "cosine")
}
centrality_similarity(df_centr_gpt4)
centrality_wrapper <- function(data, n_diag=NULL){
make_codiagnosis_graph(data, n_diagnoses = n_diag) %>%
calculate_subgraph_centrality() %>%
centrality_similarity()
}
average_cosine_matrix <- listN(
df_gpt3.5_icd_codiag,
df_gpt4.0_icd_codiag,
df_claude3_haiku_t1.0_icd_codiag,
df_claude3_opus_t1.0_icd_codiag,
df_gemini1.0_pro_t1.0_icd_codiag,
df_gemini1.5_pro_t1.0_icd_codiag
) %>%
enframe(name = "model", value = "data") %>%
mutate(data = map(data, centrality_wrapper)) %>%
unnest(data) %>%
dplyr::summarise(cosine = mean(cosine), .by = c("V1", "V2")) %>%
pivot_wider(names_from = "V2", values_from = "cosine") %>%
column_to_rownames("V1") %>%
as.matrix()
average_cosine_matrix
SLE - SLICC SLE - EULAR-ACR MCAS - Consortium MCAS - Alternative
SLE - SLICC 1.0000000 0.8540815 0.2276693 0.5103618
SLE - EULAR-ACR 0.8540815 1.0000000 0.2682776 0.4819197
MCAS - Consortium 0.2276693 0.2682776 1.0000000 0.5459547
MCAS - Alternative 0.5103618 0.4819197 0.5459547 1.0000000
custom_heatmap <- function(data,
plot_title=NULL,
legend_title=NULL,
color_scale = NULL,
midpoint = NULL,
symmetric = T,
matrix_title_size=10,
matrix_names_size=8,
legend_title_size=10,
legend_label_size=8,
dendrograms=T,
dendrogram_weight = unit(10, "mm"),
legend_orientation = "vertical",
legend_length=NULL,
grid_lines=F
){
# Determin midpoint of data
scale_max <- ifelse(symmetric,max(abs(data)),max(data))
scale_min <- ifelse(symmetric,-max(abs(data)),min(data))
scale_mid <- scale_min + (scale_max - scale_min)/2
midpoint <- ifelse(is.null(midpoint), scale_mid, midpoint)
# Set default colorscales based on symmetry of data
if (is.null(color_scale) & symmetric){color_scale <- hcl.colors(3, "Earth")}
if (is.null(color_scale) & !symmetric){color_scale <- viridis::viridis(3)}
color_function <-circlize::colorRamp2(c(scale_min,midpoint,scale_max), color_scale)
# Legend parameters
legend_params <- list(
"title_gp" = grid::gpar(fontsize = legend_title_size, fontface = "bold"),
"labels_gp" = grid::gpar(fontsize = legend_label_size),
"direction" = legend_orientation
)
# Heatmap parameters
heatmap_arguments <- list(
"matrix" = data,
"col" = color_function,
"row_names_gp" = grid::gpar(fontsize = matrix_names_size),
"column_names_gp" = grid::gpar(fontsize = matrix_names_size),
"show_column_dend"=dendrograms,
"show_row_dend"=dendrograms
)
if(!is.null(legend_title)){heatmap_arguments[["name"]] <- legend_title}
if(grid_lines){heatmap_arguments[["rect_gp"]] <- grid::gpar(col = "black", lwd = 1)}
if(dendrograms){
heatmap_arguments[['column_dend_height']] <- dendrogram_weight
heatmap_arguments[['row_dend_width']] <- dendrogram_weight
}
legend_side <- ifelse(legend_orientation=="vertical","right","bottom")
if(legend_orientation=="vertical" &!is.null(legend_length)){
legend_params[['legend_height']] <- legend_length}
if(legend_orientation=="horizontal" &!is.null(legend_length)){
legend_params[['legend_width']] <- legend_length}
# Function call
heatmap_arguments[["heatmap_legend_param"]] <- legend_params
ht <- do.call(Heatmap, heatmap_arguments)
draw(ht, heatmap_legend_side=legend_side, align_heatmap_legend = "global_center")
}
custom_heatmap(average_cosine_matrix, symmetric = F, legend_title = "Cosine similarity", grid_lines = T, dendrograms = F, legend_orientation = "horizontal", legend_length=unit(10, "cm"))

individual_cosine_matrix[1:5, 1:5]
df_gpt3.5_icd_codiag-slicc_sle df_gpt3.5_icd_codiag-eular_acr_sle df_gpt3.5_icd_codiag-mcas_consortium df_gpt3.5_icd_codiag-mcas_alternative df_gpt4.0_icd_codiag-slicc_sle
df_gpt3.5_icd_codiag-slicc_sle 1.0000000 0.8647922 0.2136086 0.4720033 0.8522184
df_gpt3.5_icd_codiag-eular_acr_sle 0.8647922 1.0000000 0.2643814 0.4613926 0.7324445
df_gpt3.5_icd_codiag-mcas_consortium 0.2136086 0.2643814 1.0000000 0.5519625 0.1663721
df_gpt3.5_icd_codiag-mcas_alternative 0.4720033 0.4613926 0.5519625 1.0000000 0.3913603
df_gpt4.0_icd_codiag-slicc_sle 0.8522184 0.7324445 0.1663721 0.3913603 1.0000000


Final plot
# n_diagnoses_bar <- 10
# n_diagnoses_abundance <- 50
# n_diagnoses_cumulative <- 50
title_size <- 9
label_size <- 6
legend_x_pad <- 4
legend_y_pad <- 2
apply_text_formatting <- list(theme(
axis.text = element_text(size = label_size),
axis.title = element_text(size = title_size),
legend.text = element_text(size = label_size-1),
strip.text = element_text(size = label_size+1),
legend.key.height = unit(0.4, 'cm'),
legend.box.background = element_rect(color = "black", size = 1),
legend.margin = margin(t = legend_y_pad, r = legend_x_pad, b = legend_y_pad, l = legend_x_pad*1.1),
legend.spacing.x = unit(0, 'cm'), # Horizontal spacing between legend items
# legend.spacing.y = unit(0, 'cm'),
# legend.box.spacing = unit(0, "cm")
))
strip_margin <- 1
strip_formatting <- list(theme(
strip.text.x = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin)),
strip.text.y = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
# strip.background = element_rect(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
))
set.seed(1234)
plt_graph_icd <-
multi_make_codiagnosis_graph(
threshold_method = "average",
top_n = 100,
layout = "stress",
df_gpt3.5_codiag,
df_gpt4.0_codiag,
df_claude3_haiku_t1.0_codiag,
df_claude3_opus_t1.0_codiag,
df_gemini1.0_pro_t1.0_codiag,
df_gemini1.5_pro_t1.0_codiag,
point_size = 1.25,
border_size = 0.25,
edge_width = 0.5,
edge_alpha = 0.5,
label_text_size = 9,
tick_text_size = 6,
highlight_stroke_multiplier = 3,
legend_height = unit(25, "pt"),
legend_width = unit(10, "pt")
)
plt_edge_icd <- multi_edge_density_plot(
df_gpt3.5_icd_codiag,
df_gpt4.0_icd_codiag,
df_claude3_haiku_t1.0_icd_codiag,
df_claude3_opus_t1.0_icd_codiag,
df_gemini1.0_pro_t1.0_icd_codiag,
df_gemini1.5_pro_t1.0_icd_codiag
) +
apply_text_formatting +
theme(legend.position = "bottom", legend.direction = "horizontal") +
guides(color = guide_legend(override.aes = list(size = 1), ncol = 2))+
scale_y_continuous(expand = expansion(mult = c(0.1,0.1)))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
plt_heatmap_icd <-
custom_heatmap(
average_cosine_matrix,
symmetric = F,
legend_title = "Cosine similarity",
grid_lines = T,
dendrograms = F,
legend_orientation = "horizontal",
legend_length = unit(2, "cm"),
matrix_names_size = 6,
legend_title_size = 7.5,
legend_label_size = 6
)


plt_fig <- cowplot::plot_grid(
NULL,
cowplot::plot_grid(
NULL, plt_graph_icd, nrow=1, rel_widths=c(0.05,0.9)
),
NULL,
cowplot::plot_grid(
plot_grid(NULL, plt_edge_icd ,rel_heights = c(0.1, 1), ncol = 1),
NULL,
cowplot::plot_grid(
NULL,
grid::grid.grabExpr(ComplexHeatmap::draw(plt_heatmap_icd, heatmap_legend_side = "bottom")),
NULL,
ncol = 1,
rel_heights = c(0.1, 1, 0.1)
),
nrow = 1,
rel_widths = c(0.5, 0.1, 0.6),
labels = c("B","","C"),
axis = 'h', align = 'bt'
),
ncol = 1,
rel_heights = c(0.05, 0.95, 0.01, 1),
labels = c("A")
)
plt_fig

LS0tCnRpdGxlOiAiQ28tZGlhZ25vc2lzIG5ldHdvcmsgYW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKYGBge3IsIG1lc3NhZ2UgPSBGfQpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlKCJ1dGlscy9kYXRhX3Byb2Nlc3NpbmcuUiIpKQpzb3VyY2UoaGVyZSgidXRpbHMvZmlndXJlcy5SIikpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCi0gR29hbCBpcyB0byBtb2RlbCBkaWFnbm9zZXMgZnJvbSBlYWNoIGNyaXRlcmlhIGJhc2VkIG9uIGhvdyBmcmVxdWVudGx5IHRoZXkgY28tb2NjdXIgdG9nZXRoZXIgd2l0aGluIGEgc2luZ2xlIDEwLXBvaW50IGRpZmZlcmVudGlhbCBkaWFnbm9zaXMgaXRlcmF0aW9uLgotIFRoZSByYXRpb25hbGUgaXMgdGhhdCBhbiBpZGVhbCBzZXQgb2YgY3JpdGVyaWEgd2lsbCB0ZW5kIHRvIHJlc3VsdCBpbiBhIHJlbGF0aXZlbHkgc2ltaWxhciBzZXQgb2YgZGlhZ25vc2VzIGluIGVhY2ggaXRlcmF0aW9uIGFuZCB0aHVzIGEgc21hbGwgc2V0IG9mIGhpZ2hseSBjby1vY2N1cnJpbmcgZGlhZ25vc2VzLgotIENvbnZlcnNlbHksIGEgcG9vciBzZXQgb2YgY3JpdGVyaWEgd2lsbCBnZW5lcmF0ZSBoaWdobHkgdmFyaWFibGUgZGlhZ25vc2VzIGluIGVhY2ggaXRlcmF0aW9uIGFuZCByYXRlcyBvZiBjby1vY2N1cnJlbmNlIHdpbGwgYmUgY29tcGFyYXRpdmVseSBsb3dlci4KCiMgSW1wb3J0IGFuZCBwcm9jZXNzIGRhdGEKCmBgYHtyLCBtZXNzYWdlID0gRn0KZGZfZ3B0My41IDwtIHJlYWRfbW9kZWwoImdwdC0zLjUtdHVyYm8tMTEwNiIsIGljZCA9IEZBTFNFKQpkZl9ncHQ0LjAgPC0gcmVhZF9tb2RlbCgiZ3B0LTQtdHVyYm8tcHJldmlldyIsIGljZCA9IEZBTFNFKQpkZl9jbGF1ZGUzX2hhaWt1X3QxLjAgPC0gcmVhZF9tb2RlbCgiY2xhdWRlLTMtaGFpa3UtMjAyNDAzMDdfdDEtMCIsIGljZCA9IEZBTFNFKQpkZl9jbGF1ZGUzX29wdXNfdDEuMCA8LSByZWFkX21vZGVsKCJjbGF1ZGUtMy1vcHVzLTIwMjQwMjI5X3QxLTAiLCBpY2QgPSBGQUxTRSkKZGZfZ2VtaW5pMS4wX3Byb190MS4wIDwtIHJlYWRfbW9kZWwoImdlbWluaS0xLjAtcHJvLTAwMl90MS0wIiwgaWNkID0gRkFMU0UpCmRmX2dlbWluaTEuNV9wcm9fdDEuMCA8LSByZWFkX21vZGVsKCJnZW1pbmktMS41LXByby0wMDFfdDEtMCIsIGljZCA9IEZBTFNFKQpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRn0KZGZfZ3B0My41X2ljZCA8LSByZWFkX21vZGVsKCJncHQtMy41LXR1cmJvLTExMDYiLCBpY2QgPSBUUlVFKQpkZl9ncHQ0LjBfaWNkIDwtIHJlYWRfbW9kZWwoImdwdC00LXR1cmJvLXByZXZpZXciLCBpY2QgPSBUUlVFKQpkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkIDwtIHJlYWRfbW9kZWwoImNsYXVkZS0zLWhhaWt1LTIwMjQwMzA3X3QxLTAiLCBpY2QgPSBUUlVFKQpkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QgPC0gcmVhZF9tb2RlbCgiY2xhdWRlLTMtb3B1cy0yMDI0MDIyOV90MS0wIiwgaWNkID0gVFJVRSkKZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCA8LSByZWFkX21vZGVsKCJnZW1pbmktMS4wLXByby0wMDJfdDEtMCIsIGljZCA9IFRSVUUpCmRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QgPC0gcmVhZF9tb2RlbCgiZ2VtaW5pLTEuNS1wcm8tMDAxX3QxLTAiLCBpY2QgPSBUUlVFKQpgYGAKCmBgYHtyfQojIFRhbGx5IGVhY2ggcGFpciBvZiBjby1vY2N1cmluZyBkaWFnbm9zZXMgd2l0aGluIGVhY2ggY3JpdGVyaWEKIyBPcmlnaW5hbCByZXNwb25zZXMKZGZfZ3B0My41X2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ3B0My41LCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2dwdDQuMF9jb2RpYWcgPC0gY3JlYXRlX2NvZGlhZ25vc2lzX2RmKGRmX2dwdDQuMCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9jbGF1ZGUzX2hhaWt1X3QxLjAsIHJlbW92ZV9zaW5nbGV0b25zID0gVCkKZGZfY2xhdWRlM19vcHVzX3QxLjBfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9jbGF1ZGUzX29wdXNfdDEuMCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9nZW1pbmkxLjBfcHJvX3QxLjBfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9nZW1pbmkxLjBfcHJvX3QxLjAsIHJlbW92ZV9zaW5nbGV0b25zID0gVCkKZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ2VtaW5pMS41X3Byb190MS4wLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmBgYAoKCmBgYHtyfQojIElDRCBjb252ZXJ0ZWQgcmVzcG9uc2VzCmRmX2dwdDMuNV9pY2RfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9ncHQzLjVfaWNkLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2dwdDQuMF9pY2RfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9ncHQ0LjBfaWNkLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2RfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2NsYXVkZTNfb3B1c190MS4wX2ljZF9jb2RpYWcgPC0gY3JlYXRlX2NvZGlhZ25vc2lzX2RmKGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkX2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkX2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpgYGAKCgpgYGB7cn0KZGZfZ3B0NC4wX2NvZGlhZwpgYGAKCiMgR3JhcGggdmlzdWFsaXphdGlvbgoKIyMgRXhwbG9yaW5nIGxheW91dHMgCgotIFRvIGRldGVybWluZSBjbGVhcmVzdCB2aXN1YWxpemF0aW9uIG9mIG5vZGVzIGFuZCBlZGdlcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQojIFNlbGVjdGluZyBhIGxheW91dAp0b3BfbiA8LSAgMjAwCnNlZWQgPC0gMTIzNAoKbGF5b3V0cyA8LSBjKCJmciIsICJkaCIsICJrayIsICJzdHJlc3MiLCAiZ3JhcGhvcHQiKQoKZ3JhcGhfdG9wX2dwdDQgPC0gbWFrZV9jb2RpYWdub3Npc19ncmFwaChkZl9ncHQ0LjBfY29kaWFnLCBuX2RpYWdub3NlcyA9IHRvcF9uKQoKZm9yIChsIGluIGxheW91dHMpewogIHNldC5zZWVkKHNlZWQpCiAgcGx0IDwtIGNlbnRyYWxpdHlfZ3JhcGgoZ3JhcGhfdG9wX2dwdDQsIGxheW91dCA9IGwpCiAgcGx0IDwtIHBsdCArIGdndGl0bGUoIkdQVDQiLCBzdWJ0aXRsZSA9IHNwcmludGYoIkxheW91dCAlcyIsIGwpKQogIHByaW50KHBsdCkKfQoKYGBgCiMjIEluZGl2aWR1YWwgbW9kZWwgZGF0YQoKIyMjIE9yaWdpbmFsIHJlc3BvbnNlcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQp0b3BfbiA8LSAgMTAwCnNlZWQgPC0gMzIxCmdyYXBoX2xheW91dCA8LSAic3RyZXNzIgoKY29kaWFnX2dyYXBoX3dyYXBwZXIgPC0gZnVuY3Rpb24oZGF0YSl7CiAgc2V0LnNlZWQoc2VlZCkKICBkYXRhIDwtIG1ha2VfY29kaWFnbm9zaXNfZ3JhcGgoZGF0YSwgbl9kaWFnbm9zZXMgPSB0b3BfbikKICBjZW50cmFsaXR5X2dyYXBoKGRhdGEsIGxheW91dCA9IGdyYXBoX2xheW91dCkKfQoKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ3B0My41X2NvZGlhZykrIGdndGl0bGUoIkNoYXRHUFQgMy41Iiwgc3VidGl0bGUgPSBzcHJpbnRmKCJUb3AgJXMiLCB0b3BfbikpCmNvZGlhZ19ncmFwaF93cmFwcGVyKGRmX2dwdDQuMF9jb2RpYWcpKyBnZ3RpdGxlKCJDaGF0R1BUIDQuMCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnKSsgZ2d0aXRsZSgiQ2xhdWRlIDMgSGFpa3UiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfY2xhdWRlM19vcHVzX3QxLjBfY29kaWFnKSsgZ2d0aXRsZSgiQ2xhdWRlIDMgT3B1cyIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9nZW1pbmkxLjBfcHJvX3QxLjBfY29kaWFnKSsgZ2d0aXRsZSgiR2VtaW5pIDEuMCBQcm8iLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZykrIGdndGl0bGUoIkdlbWluaSAxLjUgUHJvIiwgc3VidGl0bGUgPSBzcHJpbnRmKCJUb3AgJXMiLCB0b3BfbikpCmBgYAojIyMgSUNEIGNvbnZlcnRlZCByZXNwb25lcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9ncHQzLjVfaWNkX2NvZGlhZykrIGdndGl0bGUoIkNoYXRHUFQgMy41IElDRCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9ncHQ0LjBfaWNkX2NvZGlhZykrIGdndGl0bGUoIkNoYXRHUFQgNC4wIElDRCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZykrIGdndGl0bGUoIkNsYXVkZSAzIEhhaWt1IElDRCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2RfY29kaWFnKSsgZ2d0aXRsZSgiQ2xhdWRlIDMgT3B1cyBJQ0QiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZF9jb2RpYWcpKyBnZ3RpdGxlKCJHZW1pbmkgMS4wIFBybyBJQ0QiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZF9jb2RpYWcpKyBnZ3RpdGxlKCJHZW1pbmkgMS41IFBybyBJQ0QiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKYGBgCgojIyBDb21iaW5lZCBtb2RlbCBkYXRhCgojIyMgT3JpZ2luYWwgcmVzcG9uc2VzCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Niwgd2FybmluZz1GLCBtZXNzYWdlPUZ9CnNldC5zZWVkKHNlZWQpCm11bHRpX21ha2VfY29kaWFnbm9zaXNfZ3JhcGgodGhyZXNob2xkX21ldGhvZCA9ICJpbmRpdmlkdWFsIiwgdG9wX24gPSAxMDAsIGxheW91dD0ic3RyZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9ncHQzLjVfY29kaWFnLCBkZl9ncHQ0LjBfY29kaWFnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnLCBkZl9jbGF1ZGUzX29wdXNfdDEuMF9jb2RpYWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywgZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZykKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Niwgd2FybmluZz1GLCBtZXNzYWdlPUZ9Cm11bHRpX21ha2VfY29kaWFnbm9zaXNfZ3JhcGgodGhyZXNob2xkX21ldGhvZCA9ICJhdmVyYWdlIiwgdG9wX24gPSAxMDAsIGxheW91dD0ic3RyZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9ncHQzLjVfY29kaWFnLCBkZl9ncHQ0LjBfY29kaWFnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnLCBkZl9jbGF1ZGUzX29wdXNfdDEuMF9jb2RpYWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywgZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZykKYGBgCiMjIyBJQ0QgY29udmVydGVkIHJlc3BvbnNlcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQpzZXQuc2VlZChzZWVkKQptdWx0aV9tYWtlX2NvZGlhZ25vc2lzX2dyYXBoKHRocmVzaG9sZF9tZXRob2QgPSAiaW5kaXZpZHVhbCIsIHRvcF9uID0gMTAwLCBsYXlvdXQ9InN0cmVzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ3B0My41X2ljZF9jb2RpYWcsIGRmX2dwdDQuMF9pY2RfY29kaWFnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZywgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkX2NvZGlhZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkX2NvZGlhZywgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZF9jb2RpYWcpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02LCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KbXVsdGlfbWFrZV9jb2RpYWdub3Npc19ncmFwaCh0aHJlc2hvbGRfbWV0aG9kID0gImF2ZXJhZ2UiLCB0b3BfbiA9IDEwMCwgbGF5b3V0PSJzdHJlc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRmX2dwdDMuNV9pY2RfY29kaWFnLCBkZl9ncHQ0LjBfaWNkX2NvZGlhZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZF9jb2RpYWcsIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZF9jb2RpYWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZF9jb2RpYWcsIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2RfY29kaWFnKQpgYGAKCiMgRXZhbHVhdGUgZWRnZSBkZW5zaXR5CgotIEVkZ2UgZGVuc2l0eSByZXByZXNlbnRzIHRoZSB0b3RhbCBudW1iZXIgb2YgZWRnZXMgaW4gYSBncmFwaCByZWxhdGl2ZSB0byB0aGUgdG90YWwgbnVtYmVyIG9mICoqcG9zc2libGUqKiBlZGdlcyBpbiB0aGUgZ3JhcGgKICAtIFdoZW4gYWxsIHBvc3NpYmxlIGVkZ2VzIGFyZSBwcmVzZW50LCBlZGdlIGRlbnNpdHkgPSAxCiAgLSBXaGVuIG5vIGVkZ2VzIGFyZSBwcmVzZW50LCBlZGdlIGRlbnNpdHkgPSAwCi0gRGVuc2UgY28tb2NjdXJyZW5jZSBuZXR3b3JrcyByZXByZXNlbnQgY3JpdGVyaWEgdGhhdCBnZW5lcmF0ZSBoaWdobHkgcmVwcm9kdWNpYmxlIGRpYWdub3Nlcy4gQSBzcGFyc2UgY28tb2NjdXJyZW5jZSByZXByZXNlbnRzIGEgaGlnaCBkZWdyZWUgb2YgdmFyaWFiaWxpdHkgCgojIyBDb21iaW5lZCBtb2RlbCBkYXRhCgojIyMgT3JpZ2luYWwgcmVzcG9uc2VzCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9My41fQptdWx0aV9lZGdlX2RlbnNpdHlfcGxvdCgKICBkZl9ncHQzLjVfY29kaWFnLAogIGRmX2dwdDQuMF9jb2RpYWcsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2NvZGlhZywKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfY29kaWFnCikgIAoKYGBgCgojIyMgSUNEIGNvbnZlcnRlZCByZXNwb25zZXMKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0zLjV9CnBsdF9lZGdlX2ljZCA8LSBtdWx0aV9lZGdlX2RlbnNpdHlfcGxvdCgKICBkZl9ncHQzLjVfaWNkX2NvZGlhZywKICBkZl9ncHQ0LjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2RfY29kaWFnCikgIApwbHRfZWRnZV9pY2QKYGBgCgoKCiMgUGxvdCBjZW50cmFsaXR5CmBgYHtyfQpjYWxjdWxhdGVfc3ViZ3JhcGhfY2VudHJhbGl0eSA8LSBmdW5jdGlvbihnLCBjZW50cmFsaXR5X2Z1biA9ICJjZW50cmFsaXR5X2VpZ2VuIil7CiAgZGF0YS5mcmFtZShjcml0ZXJpYSA9IGcgJT4lIGFjdGl2YXRlKGVkZ2VzKSAlPiUgcHVsbChjcml0ZXJpYSkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgICBtdXRhdGUoc3ViX2dyYXBocyA9IG1hcChjcml0ZXJpYSwgZnVuY3Rpb24oYyl7CiAgICAgIGcgJT4lIGFzX2RhdGFfZnJhbWUoKSAlPiUgZmlsdGVyKGNyaXRlcmlhID09IGMpICU+JSBhc190YmxfZ3JhcGgoZGlyZWN0ZWQgPSBGKSAlPiUgCiAgICAgICAgYWN0aXZhdGUobm9kZXMpICU+JSBtdXRhdGUoY2VudHJhbGl0eSA9IGdldChjZW50cmFsaXR5X2Z1bikoKSkgJT4lIGRhdGEuZnJhbWUoKQogICAgfSkpICU+JSAKICB1bm5lc3Qoc3ViX2dyYXBocykgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAibmFtZSIsIHZhbHVlc19mcm9tID0gImNlbnRyYWxpdHkiLCB2YWx1ZXNfZmlsbCA9IDApICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoImNyaXRlcmlhIikKfQoKZ3JhcGhfYWxsX2dwdDQgPC0gbWFrZV9jb2RpYWdub3Npc19ncmFwaChkZl9ncHQ0LjBfY29kaWFnLCBuX2RpYWdub3NlcyA9IHRvcF9uKQpkZl9jZW50cl9ncHQ0IDwtIGNhbGN1bGF0ZV9zdWJncmFwaF9jZW50cmFsaXR5KGdyYXBoX2FsbF9ncHQ0KQpgYGAKCmBgYHtyfQpjZW50cmFsaXR5X3NpbWlsYXJpdHkgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUgCiAgICByb3duYW1lc190b19jb2x1bW4oImNyaXRlcmlhIikgJT4lIAogICAgZm9ybWF0X2NyaXRlcmlhKCkgJT4lIAogICAgY29sdW1uX3RvX3Jvd25hbWVzKCJjcml0ZXJpYSIpICU+JSAKICAgIGFzLm1hdHJpeCgpICU+JSAKICAgIHQoKSAlPiUgCiAgICBsc2E6OmNvc2luZSgpICU+JSAKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICByb3duYW1lc190b19jb2x1bW4oIlYxIikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKC1WMSwgbmFtZXNfdG8gPSAiVjIiLCB2YWx1ZXNfdG8gPSAiY29zaW5lIikKfQoKY2VudHJhbGl0eV9zaW1pbGFyaXR5KGRmX2NlbnRyX2dwdDQpCmBgYApgYGB7cn0KY2VudHJhbGl0eV93cmFwcGVyIDwtIGZ1bmN0aW9uKGRhdGEsIG5fZGlhZz1OVUxMKXsKICBtYWtlX2NvZGlhZ25vc2lzX2dyYXBoKGRhdGEsIG5fZGlhZ25vc2VzID0gbl9kaWFnKSAlPiUgCiAgICBjYWxjdWxhdGVfc3ViZ3JhcGhfY2VudHJhbGl0eSgpICU+JSAKICAgIGNlbnRyYWxpdHlfc2ltaWxhcml0eSgpCn0KCmF2ZXJhZ2VfY29zaW5lX21hdHJpeCA8LSBsaXN0TigKICBkZl9ncHQzLjVfaWNkX2NvZGlhZywKICBkZl9ncHQ0LjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2RfY29kaWFnCikgJT4lCiAgZW5mcmFtZShuYW1lID0gIm1vZGVsIiwgdmFsdWUgPSAiZGF0YSIpICU+JQogIG11dGF0ZShkYXRhID0gbWFwKGRhdGEsIGNlbnRyYWxpdHlfd3JhcHBlcikpICU+JQogIHVubmVzdChkYXRhKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvc2luZSA9IG1lYW4oY29zaW5lKSwgLmJ5ID0gYygiVjEiLCAiVjIiKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJWMiIsIHZhbHVlc19mcm9tID0gImNvc2luZSIpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygiVjEiKSAlPiUKICBhcy5tYXRyaXgoKQoKYXZlcmFnZV9jb3NpbmVfbWF0cml4CgoKYGBgCgoKCmBgYHtyLCBmaWcud2lkdGg9NS41LCBmaWcuaGVpZ2h0PTMuNX0KY3VzdG9tX2hlYXRtYXAgPC0gZnVuY3Rpb24oZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGU9TlVMTCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF90aXRsZT1OVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3Jfc2NhbGUgPSBOVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSBOVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ltbWV0cmljID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdHJpeF90aXRsZV9zaXplPTEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRyaXhfbmFtZXNfc2l6ZT04LAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRfdGl0bGVfc2l6ZT0xMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xhYmVsX3NpemU9OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuZHJvZ3JhbXM9VCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuZHJvZ3JhbV93ZWlnaHQgPSB1bml0KDEwLCAibW0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX29yaWVudGF0aW9uID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xlbmd0aD1OVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICBncmlkX2xpbmVzPUYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKXsKICAKICAjIERldGVybWluIG1pZHBvaW50IG9mIGRhdGEKICBzY2FsZV9tYXggPC0gaWZlbHNlKHN5bW1ldHJpYyxtYXgoYWJzKGRhdGEpKSxtYXgoZGF0YSkpCiAgc2NhbGVfbWluIDwtIGlmZWxzZShzeW1tZXRyaWMsLW1heChhYnMoZGF0YSkpLG1pbihkYXRhKSkKICBzY2FsZV9taWQgPC0gc2NhbGVfbWluICsgKHNjYWxlX21heCAtIHNjYWxlX21pbikvMgogIAogIG1pZHBvaW50IDwtIGlmZWxzZShpcy5udWxsKG1pZHBvaW50KSwgc2NhbGVfbWlkLCBtaWRwb2ludCkKICAKICAjIFNldCBkZWZhdWx0IGNvbG9yc2NhbGVzIGJhc2VkIG9uIHN5bW1ldHJ5IG9mIGRhdGEKICBpZiAoaXMubnVsbChjb2xvcl9zY2FsZSkgJiBzeW1tZXRyaWMpe2NvbG9yX3NjYWxlIDwtIGhjbC5jb2xvcnMoMywgIkVhcnRoIil9CiAgaWYgKGlzLm51bGwoY29sb3Jfc2NhbGUpICYgIXN5bW1ldHJpYyl7Y29sb3Jfc2NhbGUgPC0gdmlyaWRpczo6dmlyaWRpcygzKX0KICBjb2xvcl9mdW5jdGlvbiA8LWNpcmNsaXplOjpjb2xvclJhbXAyKGMoc2NhbGVfbWluLG1pZHBvaW50LHNjYWxlX21heCksIGNvbG9yX3NjYWxlKQogIAogICMgTGVnZW5kIHBhcmFtZXRlcnMKICBsZWdlbmRfcGFyYW1zIDwtIGxpc3QoCiAgICAgICJ0aXRsZV9ncCIgPSBncmlkOjpncGFyKGZvbnRzaXplID0gbGVnZW5kX3RpdGxlX3NpemUsIGZvbnRmYWNlID0gImJvbGQiKSwKICAgICAgImxhYmVsc19ncCIgPSBncmlkOjpncGFyKGZvbnRzaXplID0gbGVnZW5kX2xhYmVsX3NpemUpLAogICAgICAiZGlyZWN0aW9uIiA9IGxlZ2VuZF9vcmllbnRhdGlvbgogICAgICApCiAgCiAgIyBIZWF0bWFwIHBhcmFtZXRlcnMKICBoZWF0bWFwX2FyZ3VtZW50cyA8LSBsaXN0KAogICAgIm1hdHJpeCIgPSBkYXRhLAogICAgImNvbCIgPSBjb2xvcl9mdW5jdGlvbiwKICAgICJyb3dfbmFtZXNfZ3AiID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IG1hdHJpeF9uYW1lc19zaXplKSwKICAgICJjb2x1bW5fbmFtZXNfZ3AiID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IG1hdHJpeF9uYW1lc19zaXplKSwKICAgICJzaG93X2NvbHVtbl9kZW5kIj1kZW5kcm9ncmFtcywKICAgICJzaG93X3Jvd19kZW5kIj1kZW5kcm9ncmFtcwogICkKICAKICBpZighaXMubnVsbChsZWdlbmRfdGl0bGUpKXtoZWF0bWFwX2FyZ3VtZW50c1tbIm5hbWUiXV0gPC0gbGVnZW5kX3RpdGxlfQogIGlmKGdyaWRfbGluZXMpe2hlYXRtYXBfYXJndW1lbnRzW1sicmVjdF9ncCJdXSA8LSBncmlkOjpncGFyKGNvbCA9ICJibGFjayIsIGx3ZCA9IDEpfQogIGlmKGRlbmRyb2dyYW1zKXsKICAgIGhlYXRtYXBfYXJndW1lbnRzW1snY29sdW1uX2RlbmRfaGVpZ2h0J11dIDwtIGRlbmRyb2dyYW1fd2VpZ2h0CiAgICBoZWF0bWFwX2FyZ3VtZW50c1tbJ3Jvd19kZW5kX3dpZHRoJ11dIDwtIGRlbmRyb2dyYW1fd2VpZ2h0CiAgfQogIAogIGxlZ2VuZF9zaWRlIDwtIGlmZWxzZShsZWdlbmRfb3JpZW50YXRpb249PSJ2ZXJ0aWNhbCIsInJpZ2h0IiwiYm90dG9tIikKICBpZihsZWdlbmRfb3JpZW50YXRpb249PSJ2ZXJ0aWNhbCIgJiFpcy5udWxsKGxlZ2VuZF9sZW5ndGgpKXsKICAgIGxlZ2VuZF9wYXJhbXNbWydsZWdlbmRfaGVpZ2h0J11dIDwtIGxlZ2VuZF9sZW5ndGh9CiAgaWYobGVnZW5kX29yaWVudGF0aW9uPT0iaG9yaXpvbnRhbCIgJiFpcy5udWxsKGxlZ2VuZF9sZW5ndGgpKXsKICAgIGxlZ2VuZF9wYXJhbXNbWydsZWdlbmRfd2lkdGgnXV0gPC0gbGVnZW5kX2xlbmd0aH0KICAjIEZ1bmN0aW9uIGNhbGwKICBoZWF0bWFwX2FyZ3VtZW50c1tbImhlYXRtYXBfbGVnZW5kX3BhcmFtIl1dIDwtIGxlZ2VuZF9wYXJhbXMKICBodCA8LSBkby5jYWxsKEhlYXRtYXAsIGhlYXRtYXBfYXJndW1lbnRzKQogIGRyYXcoaHQsIGhlYXRtYXBfbGVnZW5kX3NpZGU9bGVnZW5kX3NpZGUsIGFsaWduX2hlYXRtYXBfbGVnZW5kID0gImdsb2JhbF9jZW50ZXIiKQogIAp9CgpjdXN0b21faGVhdG1hcChhdmVyYWdlX2Nvc2luZV9tYXRyaXgsIHN5bW1ldHJpYyA9IEYsIGxlZ2VuZF90aXRsZSA9ICJDb3NpbmUgc2ltaWxhcml0eSIsIGdyaWRfbGluZXMgPSBULCBkZW5kcm9ncmFtcyA9IEYsIGxlZ2VuZF9vcmllbnRhdGlvbiA9ICJob3Jpem9udGFsIiwgbGVnZW5kX2xlbmd0aD11bml0KDEwLCAiY20iKSkKYGBgCgoKYGBge3J9CmluZGl2aWR1YWxfY29zaW5lX21hdHJpeCA8LSBjb21iaW5lX2RhdGFfZnJhbWVzKAogIGRmX2dwdDMuNV9pY2RfY29kaWFnLAogIGRmX2dwdDQuMF9pY2RfY29kaWFnLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2RfY29kaWFnLAogIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZF9jb2RpYWcsCiAgYWRkaXRpb25hbF9mdW5jdGlvbiA9IGZ1bmN0aW9uKHgpewogICAgeCAlPiUgCiAgICAgIG1ha2VfY29kaWFnbm9zaXNfZ3JhcGgoKSAlPiUgCiAgICAgIGNhbGN1bGF0ZV9zdWJncmFwaF9jZW50cmFsaXR5KCkgJT4lIAogICAgICByb3duYW1lc190b19jb2x1bW4oImNyaXRlcmlhIikgJT4lIAogICAgICBwaXZvdF9sb25nZXIoLWNyaXRlcmlhLCBuYW1lc190bz0iZGlhZ25vc2lzIiwgdmFsdWVzX3RvPSJjZW50cmFsaXR5IikKICB9CikgJT4lIAogIHVuaXRlKGNyaXRlcmlhLCBvcmlnaW5hbF9kZiwgY3JpdGVyaWEsIHNlcCA9ICItIikgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiZGlhZ25vc2lzIiwgdmFsdWVzX2Zyb209ImNlbnRyYWxpdHkiLCB2YWx1ZXNfZmlsbD0wKSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJjcml0ZXJpYSIpICU+JSAKICBhcy5tYXRyaXgoKSAlPiUgCiAgICB0KCkgJT4lIAogICAgbHNhOjpjb3NpbmUoKSAlPiUgCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCJWMSIpICU+JSAKICAgIHBpdm90X2xvbmdlcigtVjEsIG5hbWVzX3RvID0gIlYyIiwgdmFsdWVzX3RvID0gImNvc2luZSIpICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gIlYyIiwgdmFsdWVzX2Zyb20gPSAiY29zaW5lIikgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJWMSIpICU+JQogIGFzLm1hdHJpeCgpCgppbmRpdmlkdWFsX2Nvc2luZV9tYXRyaXhbMTo1LCAxOjVdCmBgYAoKYGBge3IsIGZpZy53aWR0aD01LjUsIGZpZy5oZWlnaHQ9My41fQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpjdXN0b21faGVhdG1hcDIgPC0gZnVuY3Rpb24oZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3Jfc2NhbGUgPSBoY2wuY29sb3JzKDMsICJFYXJ0aCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSBOVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ltbWV0cmljID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRfc2l6ZT0xMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuZHJvZ3JhbV93ZWlnaHQgPSB1bml0KDEwLCAibW0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX3BhcmFtcyA9IGxpc3QoTlVMTCkpewogIHNjYWxlX21heCA8LSBpZmVsc2Uoc3ltbWV0cmljLG1heChhYnMoZGF0YSkpLG1heChkYXRhKSkKICBzY2FsZV9taW4gPC0gaWZlbHNlKHN5bW1ldHJpYywtbWF4KGFicyhkYXRhKSksbWluKGRhdGEpKQogIHNjYWxlX21pZCA8LSBzY2FsZV9taW4gKyAoc2NhbGVfbWF4IC0gc2NhbGVfbWluKS8yCiAgCiAgbWlkcG9pbnQgPC0gaWZlbHNlKGlzLm51bGwobWlkcG9pbnQpLCBzY2FsZV9taWQsIG1pZHBvaW50KQogIAogIGNvbG9yX2Z1bmN0aW9uIDwtY2lyY2xpemU6OmNvbG9yUmFtcDIoYyhzY2FsZV9taW4sbWlkcG9pbnQsc2NhbGVfbWF4KSwgY29sb3Jfc2NhbGUpCiAgCiAgIyBsZWdlbmRfcGFyYW1zIDwtIGFwcGVuZCgKICAjICAgbGlzdCgKICAjICAgICB0aXRsZV9ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSBmb250X3NpemUpLAogICMgICAgIGxhYmVsc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSBmb250X3NpemUqMC44KSksIAogICMgICBsZWdlbmRfcGFyYW1zKQogIAoKICBhbm5vdGF0aW9uX2RhdGEgPC0gZGF0YS5mcmFtZSgKICByb3duYW1lcyA9IHJvd25hbWVzKGNvc2luZV9kYXRhKSwKICBtb2RlbCA9IHN0cl9leHRyYWN0KHJvd25hbWVzKGNvc2luZV9kYXRhKSwgImdwdDMuNXxncHQ0LjB8Y2xhdWRlM19oYWlrdXxjbGF1ZGUzX29wdXN8Z2VtaW5pMS4wX3Byb3xnZW1pbmkxLjVfcHJvIiksCiAgY3JpdGVyaWEgPSBzdHJfZXh0cmFjdChyb3duYW1lcyhjb3NpbmVfZGF0YSksICJzbGljY19zbGV8ZXVsYXJfYWNyX3NsZXxtY2FzX2NvbnNvcnRpdW18bWNhc19hbHRlcm5hdGl2ZSIpCikgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygicm93bmFtZXMiKSAlPiUgCiAgZm9ybWF0X2NyaXRlcmlhKCkgJT4lIAogIGZvcm1hdF9tb2RlbHMoKQoKICAjIG1vZGVsX2NvbG9ycyA8LSBicmV3ZXIucGFsKDcsICJEYXJrMiIpWy02XQogIG1vZGVsX2NvbG9ycyA8LSBjb2xvcnNwYWNlOjpsaWdodGVuKGJyZXdlci5wYWwoNiwgIkRhcmsyIiksIGFtb3VudCA9IC0wLjEpCiAgbmFtZXMobW9kZWxfY29sb3JzKSA8LSB1bmlxdWUoYW5ub3RhdGlvbl9kYXRhJG1vZGVsKQogIAogIGNyaXRlcmlhX2NvbG9ycyA8LSBjb2xvcnNwYWNlOjpsaWdodGVuKGJyZXdlci5wYWwoNCwgIlNldDEiKSwgYW1vdW50ID0gMC4zKQogIG5hbWVzKGNyaXRlcmlhX2NvbG9ycykgPC0gdW5pcXVlKGFubm90YXRpb25fZGF0YSRjcml0ZXJpYSkKICAKICBjb2xfYW5ub3RhdGlvbiA8LSBIZWF0bWFwQW5ub3RhdGlvbigKICAgIE1vZGVsID0gYW5ub3RhdGlvbl9kYXRhJG1vZGVsLAogICAgQ3JpdGVyaWEgPSBhbm5vdGF0aW9uX2RhdGEkY3JpdGVyaWEsCiAgICBjb2wgPSBsaXN0KE1vZGVsID0gbW9kZWxfY29sb3JzLCBDcml0ZXJpYSA9IGNyaXRlcmlhX2NvbG9ycykKICApCiAgCiAgQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAoCiAgICBkYXRhLAogICAgY29sID0gY29sb3JfZnVuY3Rpb24sCiAgICB0b3BfYW5ub3RhdGlvbiA9IGNvbF9hbm5vdGF0aW9uLAogICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgIHNob3dfY29sdW1uX25hbWVzID0gRkFMU0UsCiAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgIAogICAgY29sdW1uX3RpdGxlID0gdGl0bGUsCiAgICBuYW1lID0gbWV0cmljLAogICAgY29sdW1uX2RlbmRfaGVpZ2h0ICA9IGRlbmRyb2dyYW1fd2VpZ2h0LAogICAgcm93X2RlbmRfd2lkdGggPSBkZW5kcm9ncmFtX3dlaWdodCwKICApCiAgCn0KICAKYWx0X2hlYXRtYXAgPC0gY3VzdG9tX2hlYXRtYXAyKGluZGl2aWR1YWxfY29zaW5lX21hdHJpeCwgCiAgICAgICAgICAgICAgICBjb2xvcl9zY2FsZSA9IHZpcmlkaXM6OnZpcmlkaXMoMyksIAogICAgICAgICAgICAgICAgdGl0bGUgPSAiICIsIAogICAgICAgICAgICAgICAgbWV0cmljID0gIkNvc2luZVxuc2ltaWxhcml0eSIsIAogICAgICAgICAgICAgICAgc3ltbWV0cmljID0gRiwKICAgICAgICAgICAgICAgIGZvbnRfc2l6ZSA9IDgpICAKICAKCmFsdF9oZWF0bWFwCmBgYApgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NH0KcGx0X2FsdCA8LSBwbG90X2dyaWQoZ3JpZDo6Z3JpZC5ncmFiRXhwcihDb21wbGV4SGVhdG1hcDo6ZHJhdyhhbHRfaGVhdG1hcCkpKQpwbHRfYWx0CmBgYApgYGB7cn0KZ2dzYXZlKGhlcmUoImZpZ3VyZXMvNF9hbHRfaGVhdG1hcC5wZGYiKSwgcGxvdD1wbHRfYWx0LCBoZWlnaHQgPSA0LCB3aWR0aCA9IDYpCmBgYAoKIyBGaW5hbCBwbG90CgpgYGB7ciwgZmlnLndpZHRoPTMuNSwgZmlnLmhlaWdodD01LjV9CiMgbl9kaWFnbm9zZXNfYmFyIDwtIDEwCiMgbl9kaWFnbm9zZXNfYWJ1bmRhbmNlIDwtIDUwCiMgbl9kaWFnbm9zZXNfY3VtdWxhdGl2ZSA8LSA1MAoKdGl0bGVfc2l6ZSA8LSA5CmxhYmVsX3NpemUgPC0gNgpsZWdlbmRfeF9wYWQgPC0gNApsZWdlbmRfeV9wYWQgPC0gMgoKYXBwbHlfdGV4dF9mb3JtYXR0aW5nIDwtIGxpc3QodGhlbWUoCiAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplKSwKICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSB0aXRsZV9zaXplKSwKICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gbGFiZWxfc2l6ZS0xKSwKICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplKzEpLAogIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgwLjQsICdjbScpLAogIGxlZ2VuZC5ib3guYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJibGFjayIsIHNpemUgPSAxKSwKICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKHQgPSBsZWdlbmRfeV9wYWQsIHIgPSBsZWdlbmRfeF9wYWQsIGIgPSBsZWdlbmRfeV9wYWQsIGwgPSBsZWdlbmRfeF9wYWQqMS4xKSwKICBsZWdlbmQuc3BhY2luZy54ID0gdW5pdCgwLCAnY20nKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEhvcml6b250YWwgc3BhY2luZyBiZXR3ZWVuIGxlZ2VuZCBpdGVtcwogICMgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMCwgJ2NtJyksCiAgIyBsZWdlbmQuYm94LnNwYWNpbmcgPSB1bml0KDAsICJjbSIpCiAgKSkKCnN0cmlwX21hcmdpbiA8LSAxCnN0cmlwX2Zvcm1hdHRpbmcgPC0gbGlzdCh0aGVtZSgKICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSBzdHJpcF9tYXJnaW4sIHIgPSBzdHJpcF9tYXJnaW4sIGIgPSBzdHJpcF9tYXJnaW4sIGwgPSBzdHJpcF9tYXJnaW4pKSwgCiAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gc3RyaXBfbWFyZ2luLCByID0gc3RyaXBfbWFyZ2luLCBiID0gc3RyaXBfbWFyZ2luLCBsID0gc3RyaXBfbWFyZ2luKSkKICAjIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QobWFyZ2luID0gbWFyZ2luKHQgPSBzdHJpcF9tYXJnaW4sIHIgPSBzdHJpcF9tYXJnaW4sIGIgPSBzdHJpcF9tYXJnaW4sIGwgPSBzdHJpcF9tYXJnaW4pKQopKQoKc2V0LnNlZWQoMTIzNCkKcGx0X2dyYXBoX2ljZCA8LQogIG11bHRpX21ha2VfY29kaWFnbm9zaXNfZ3JhcGgoCiAgICB0aHJlc2hvbGRfbWV0aG9kID0gImF2ZXJhZ2UiLAogICAgdG9wX24gPSAxMDAsCiAgICBsYXlvdXQgPSAic3RyZXNzIiwKICAgIGRmX2dwdDMuNV9jb2RpYWcsCiAgICBkZl9ncHQ0LjBfY29kaWFnLAogICAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2NvZGlhZywKICAgIGRmX2NsYXVkZTNfb3B1c190MS4wX2NvZGlhZywKICAgIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9jb2RpYWcsCiAgICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfY29kaWFnLAogICAgcG9pbnRfc2l6ZSA9IDEuMjUsCiAgICBib3JkZXJfc2l6ZSA9IDAuMjUsCiAgICBlZGdlX3dpZHRoID0gMC41LAogICAgZWRnZV9hbHBoYSA9IDAuNSwKICAgIGxhYmVsX3RleHRfc2l6ZSA9IDksCiAgICB0aWNrX3RleHRfc2l6ZSA9IDYsCiAgICBoaWdobGlnaHRfc3Ryb2tlX211bHRpcGxpZXIgPSAzLAogICAgbGVnZW5kX2hlaWdodCA9IHVuaXQoMjUsICJwdCIpLAogICAgbGVnZW5kX3dpZHRoID0gdW5pdCgxMCwgInB0IikKICApIAoKcGx0X2VkZ2VfaWNkIDwtIG11bHRpX2VkZ2VfZGVuc2l0eV9wbG90KAogIGRmX2dwdDMuNV9pY2RfY29kaWFnLAogIGRmX2dwdDQuMF9pY2RfY29kaWFnLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2RfY29kaWFnLAogIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZF9jb2RpYWcKKSAgKwogIGFwcGx5X3RleHRfZm9ybWF0dGluZyArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMSksIG5jb2wgPSAyKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLjEsMC4xKSkpCgpwbHRfaGVhdG1hcF9pY2QgPC0KICBjdXN0b21faGVhdG1hcCgKICAgIGF2ZXJhZ2VfY29zaW5lX21hdHJpeCwKICAgIHN5bW1ldHJpYyA9IEYsCiAgICBsZWdlbmRfdGl0bGUgPSAiQ29zaW5lIHNpbWlsYXJpdHkiLAogICAgZ3JpZF9saW5lcyA9IFQsCiAgICBkZW5kcm9ncmFtcyA9IEYsCiAgICBsZWdlbmRfb3JpZW50YXRpb24gPSAiaG9yaXpvbnRhbCIsCiAgICBsZWdlbmRfbGVuZ3RoID0gdW5pdCgyLCAiY20iKSwKICAgIG1hdHJpeF9uYW1lc19zaXplID0gNiwgCiAgICBsZWdlbmRfdGl0bGVfc2l6ZSA9IDcuNSwKICAgIGxlZ2VuZF9sYWJlbF9zaXplID0gNgogICkKCgpwbHRfZmlnIDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZCgKICBOVUxMLCAKICBjb3dwbG90OjpwbG90X2dyaWQoCiAgICBOVUxMLCBwbHRfZ3JhcGhfaWNkLCBucm93PTEsIHJlbF93aWR0aHM9YygwLjA1LDAuOSkKICApLAogIE5VTEwsCiAgY293cGxvdDo6cGxvdF9ncmlkKAogICAgcGxvdF9ncmlkKE5VTEwsIHBsdF9lZGdlX2ljZCAscmVsX2hlaWdodHMgPSBjKDAuMSwgMSksIG5jb2wgPSAxKSwKICAgIE5VTEwsCiAgICBjb3dwbG90OjpwbG90X2dyaWQoCiAgICAgIE5VTEwsCiAgICAgIGdyaWQ6OmdyaWQuZ3JhYkV4cHIoQ29tcGxleEhlYXRtYXA6OmRyYXcocGx0X2hlYXRtYXBfaWNkLCBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIpKSwKICAgICAgTlVMTCwKICAgICAgbmNvbCA9IDEsCiAgICAgIHJlbF9oZWlnaHRzID0gYygwLjEsIDEsIDAuMSkKICAgICksCiAgICBucm93ID0gMSwKICAgIHJlbF93aWR0aHMgPSBjKDAuNSwgMC4xLCAwLjYpLAogICAgbGFiZWxzID0gYygiQiIsIiIsIkMiKSwKICAgIGF4aXMgPSAnaCcsIGFsaWduID0gJ2J0JwogICksCgogIG5jb2wgPSAxLAogIHJlbF9oZWlnaHRzID0gYygwLjA1LCAwLjk1LCAwLjAxLCAxKSwKICBsYWJlbHMgPSBjKCJBIikKKQoKcGx0X2ZpZyAKYGBgCmBgYHtyfQpnZ3NhdmUoaGVyZSgiZmlndXJlcy80X05ldHdvcmtfYW5hbHlzaXMucGRmIiksIHBsb3Q9cGx0X2ZpZywgaGVpZ2h0ID0gNS41LCB3aWR0aCA9IDMuNSkKYGBgCgo=